package net.tv90.bfmccoreqqbot.api;

import com.alibaba.fastjson.JSONObject;
import net.tv90.bfmccoreqqbot.api.annotation.AdminOnly;
import net.tv90.bfmccoreqqbot.api.annotation.MessageCheck;
import net.tv90.bfmccoreqqbot.api.entity.MessageContext;
import net.tv90.bfmccoreqqbot.api.entity.gocqhttp.Params;
import net.tv90.bfmccoreqqbot.api.entity.gocqhttp.Request;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public abstract class AbstractMessageDispatcher implements MessageDispatcher {

    private static final ExecutorService executorService = new ThreadPoolExecutor(
            5, 8, 120L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10));
    protected final Map<String, Class<? extends MessageHandler>> handlers = new HashMap<>();
    protected final List<HandlerInterceptor> interceptors = new ArrayList<>();
    protected MessageSender sender = new DefaultMessageSender();

    protected void register(String messageType, Class<? extends MessageHandler> handlerClass) {
        handlers.put(messageType, handlerClass);
    }

    public void registerInterceptor(HandlerInterceptor interceptor) {
        interceptors.add(interceptor);
    }

    @Override
    public void dispatch(MessageContext msg) {
        executorService.submit(() -> {
            String message = msg.getMessage();
            for (HandlerInterceptor interceptor : interceptors) {
                if (!interceptor.preHandle(msg)) {
                    return;
                }
            }
            handlers.forEach((k, v) -> {
                if (v.isAnnotationPresent(MessageCheck.class)) {
                    switch (v.getAnnotation(MessageCheck.class).value()) {
                        case CONTAINS: {
                            if (message.contains(k)) {
                                preHandleMessage(msg, v);
                            }
                        }
                        break;
                        case EQUALS: {
                            if (message.equals(k)) {
                                preHandleMessage(msg, v);
                            }
                        }
                        break;
                        case STARTS_WITH: {
                            if (message.startsWith(k)) {
                                preHandleMessage(msg, v);
                            }
                        }
                        break;
                        case ENDS_WITH: {
                            if (message.endsWith(k)) {
                                preHandleMessage(msg, v);
                            }
                        }
                        break;
                        case REGEX: {
                            if (message.matches(k)) {
                                preHandleMessage(msg, v);
                            }
                        }
                    }
                }
            });
            for (int i = interceptors.size() - 1; i >= 0; i--) {
                HandlerInterceptor interceptor = interceptors.get(i);
                interceptor.postHandle(msg);
            }
        });
    }

    private void preHandleMessage(MessageContext msg, Class<? extends MessageHandler> handlerClass) {

        if (handlerClass.isAnnotationPresent(AdminOnly.class)) {
            if (!msg.getSenderRole().equals("admin") && !msg.getSenderRole().equals("owner")) {
                return;
            }
        }

        try {
            MessageHandler handler = handlerClass.getDeclaredConstructor().newInstance();

            if (!handler.onPreHandleMessage(msg)) {
                return;
            }
            String result = handler.handleMessage(msg);
            handler.onPostHandleMessage(msg);

            if (result != null) {
                preSendMessage(result, msg, handler);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void preSendMessage(String result, MessageContext msg, MessageHandler handler) {

        if (!handler.onPreSendMessage(result, msg, handler)) {
            return;
        }

        Request<Params> request = new Request<>();
        request.setAction("send_msg");
        Params params = new Params();
        params.setMessage_type("group");
        params.setMessage(result);
        params.setGroup_id(msg.getSenderGroupQq());
        params.setAuto_escape(false);
        request.setParams(params);

        sender.sendMessage(JSONObject.toJSONString(request));
        handler.onPostSendMessage(result, msg, handler);
    }

    public Map<String, Class<? extends MessageHandler>> getHandlers() {
        return handlers;
    }

}
